using System.Reflection;
using System;
using System.Collections.Generic;
using System.Collections;

namespace foundation
{

    public class CopyEditItem
    {
        public object pre;
        public object now;
        public object target;
        public CopyEditItem(object pre, object now)
        {
            this.pre = pre;
            this.now = now;
        }
    }


    public static class DataConvert
    {
        public static T ConvertAMF3Data<T>(object obj, Type type = null)
        {

            var objtype = obj.GetType();
            if (null == type) type = typeof(T);

            if (objtype == type)
            {
                return (T)obj;
            }

            var instance = (T)Activator.CreateInstance(type);


            foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)obj)
            {
                var info = type.GetField(entry.Key);
                if (null != info)
                {
                    setValue(info, instance, entry.Value);
                }
            }

            return instance;
        }


        public static void setValue(FieldInfo fi, object instance, object value)
        {
            if (value != null && ReflectionUtils.IsSubClass(value.GetType(), fi.FieldType))
            {
                fi.SetValue(instance, value);
            }
            else
            {
                try
                {

                    if (value is Array)
                    {
                        value = ArrayToList((object[])value, fi.FieldType);
                    }
                    else if (isDictionary(fi.FieldType))
                    {
                        value = ObjectDictToVODict<object>((Dictionary<string, object>)value, fi.FieldType);
                    }


                    if (value == null)
                    {
                        if (false == isNumber(fi.FieldType))
                        {
                            value = Convert.ChangeType(value, fi.FieldType);
                        }
                    }
                    else
                    {
                        value = Convert.ChangeType(value, fi.FieldType);
                    }


                }
                catch (Exception e)
                {
                    Console.Write(e.StackTrace);
                    throw new Exception($"ChangeType from value:{value} to {fi.FieldType.Name} failed");
                }

                fi.SetValue(instance, value);
            }
        }

        public static object ArrayToList(object[] obj, Type type)
        {
            var list = Activator.CreateInstance(type);
            var add = type.GetMethod("Add");
            var objs = new object[] { null };
            for (var i = 0; i < obj.Length; i++)
            {
                var v = obj[i];
                var childtype = type.GenericTypeArguments[0];
                if (v is Array)
                {
                    v = ArrayToList((object[])v, childtype);
                }
                else
                {
                    if (childtype != typeof(object) && childtype != v.GetType())
                    {
                        v = ConvertAMF3Data<object>(v, childtype);
                    }


                }




                objs[0] = v;
                try
                {
                    add.Invoke(list, objs);
                }
                catch (System.Exception)
                {

                    throw;
                }

            }
            return list;
        }


        public static T ObjectDictToVODict<T>(Dictionary<string, object> value, Type type = null)
        {
            if (null == type) type = typeof(T);
            var dict = Activator.CreateInstance(type);
            var add = type.GetMethod("Add");
            var x = type.GenericTypeArguments[1];

            var objs = new object[] { "", null };
            foreach (KeyValuePair<string, object> entry in value)
            {
                objs[0] = entry.Key;
                object entryValue = entry.Value;
                if (x != entry.Value.GetType())
                {
                    if (DataConvert.isDictionary(x))
                    {
                        entryValue = DataConvert.ObjectDictToVODict<object>((ASObject)entry.Value, x);
                    }
                    else
                    {
                        entryValue = DataConvert.ConvertAMF3Data<object>(entry.Value, x);
                    }
                }


                objs[1] = entryValue;
                add.Invoke(dict, objs);
            }

            return (T)dict;
        }


        public static object tryGetValue(object target, string key)
        {
            if (isDictionary(target.GetType()))
            {
                return ((IDictionary)target)[key];
            }
            else
            {
                var fi = target.GetType().GetField(key);
                if (null != fi)
                {
                    return fi.GetValue(target);
                }
            }


            return null;
        }


        private static Type dictionaryType = typeof(Dictionary<,>);
        public static bool isDictionary(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == dictionaryType;
        }


        private static Dictionary<Type, bool> numberType;

        public static Dictionary<Type, bool> GetNumberType()
        {

            if (null == numberType)
            {
                numberType = new Dictionary<Type, bool>();

                numberType.Add(typeof(short), true);
                numberType.Add(typeof(ushort), true);
                numberType.Add(typeof(int), true);
                numberType.Add(typeof(uint), true);
                numberType.Add(typeof(float), true);
                numberType.Add(typeof(byte), true);
                numberType.Add(typeof(double), true);

            }


            return numberType;
        }


        public static bool isNumber(object value)
        {
            var numberType = GetNumberType();
            return numberType.ContainsKey(value.GetType());
        }

        public static bool isNumber(Type value)
        {
            var numberType = GetNumberType();
            return numberType.ContainsKey(value);
        }

        private static Type asType = typeof(ASObject);

        public static void CopyProperty(ASObject from, object to, ASObject log = null)
        {
            void breakEdit(string p, object pre, object now)
            {
                log[p] = new CopyEditItem(pre, now);
            }



            var numberType = GetNumberType();
            var toType = to.GetType();
            // var asType = typeof(ASObject);
            foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)from)
            {

                var key = entry.Key;
                var value = entry.Value;
                if (null == value)
                {
                    if (toType == asType)
                    {
                        ((ASObject)to).Remove(key);
                    }
                    else
                    {

                        if (true == DataConvert.isDictionary(toType))
                        {
                            var pre = ((IDictionary)to)[key];
                            ((IDictionary)to).Remove(key);
                            breakEdit(key, pre, value);
                        }
                        else
                        {
                            var fi = toType.GetField(key);
                            if (null != fi)
                            {
                                if (null != log)
                                {
                                    var pre = fi.GetValue(to);
                                    breakEdit(key, pre, value);
                                }
                                fi.SetValue(to, value);


                            }
                        }

                    }
                }
                else
                {
                    var valuetype = value.GetType();
                    var nstype = numberType.ContainsKey(valuetype) || value is string;

                    if (toType == asType)
                    {
                        if (nstype)
                        {
                            if (null != log)
                            {
                                object pre;
                                ((ASObject)to).TryGetValue(key, out pre);
                                breakEdit(key, pre, value);
                            }

                            ((ASObject)to)[key] = value;
                        }
                        else
                        {
                            var newto = ((ASObject)to)[key];
                            if (null == newto)
                            {
                                newto = ((ASObject)to)[key] = new ASObject();
                            }

                            ASObject newlog = null;
                            if (null != log)
                            {
                                log[key] = newlog = new ASObject();
                            }
                            CopyProperty((ASObject)value, newto, newlog);
                        }

                    }
                    else
                    {

                        if (isDictionary(toType))
                        {
                            var prevalue = ((IDictionary)to)[key];
                            var DictValueType = toType.GenericTypeArguments[1];
                            object newto = prevalue;
                            if (null == prevalue)
                            {
                                if (DictValueType == valuetype)
                                {

                                    if (null != log)
                                    {
                                        breakEdit(key, null, value);
                                    }
                                    ((IDictionary)to).Add(key, value);
                                }
                                else
                                {
                                    newto = Activator.CreateInstance(DictValueType);
                                    if (null != log)
                                    {
                                        breakEdit(key, null, newto);
                                    }
                                    ((IDictionary)to).Add(key, newto);
                                }
                            }

                            if (null != newto)
                            {

                                if (DictValueType == valuetype)
                                {
                                    if (null != prevalue)
                                    {
                                        if (null != log)
                                        {
                                            breakEdit(key, prevalue, value);
                                        }
                                        ((IDictionary)to).Remove(key);
                                        ((IDictionary)to).Add(key, value);
                                    }

                                }
                                else
                                {
                                    ASObject newlog = null;
                                    if (null != log)
                                    {

                                        log[key] = newlog = new ASObject();
                                    }

                                    CopyProperty((ASObject)value, newto, newlog);
                                }


                            }
                        }
                        else
                        {
                            var fi = toType.GetField(key);
                            if (null != fi)
                            {

                                if (nstype || fi.FieldType == valuetype)
                                {
                                    if (null != log)
                                    {
                                        var pre = fi.GetValue(to);
                                        breakEdit(key, pre, value);
                                    }
                                    fi.SetValue(to, Convert.ChangeType(value, fi.FieldType));
                                }
                                else
                                {
                                    var newto = fi.GetValue(to);
                                    if (null == newto)
                                    {
                                        newto = Activator.CreateInstance(fi.FieldType);
                                        if (null != log)
                                        {
                                            breakEdit(key, null, newto);
                                        }
                                        fi.SetValue(to, newto);
                                    }

                                    ASObject newlog = null;

                                    if (null != log)
                                    {
                                        log[key] = newlog = new ASObject();
                                    }

                                    CopyProperty((ASObject)value, newto, newlog);
                                }
                            }
                        }
                    }
                }
            }
        }


        public static void DispatchCopyEdit(ASObject log, object to, string p = "", string p2 = "")
        {
            var dp1 = p;
            var dp2 = p2;
            var type = to.GetType();
            foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)log)
            {
                var key = entry.Key;
                var value = entry.Value;

                if (value is CopyEditItem)
                {

                    if (isDictionary(type))
                    {
                        (value as CopyEditItem).target = to;
                        p = dp1.Substring(0, dp1.Length - 1);
                    }
                    else
                    {
                        p = dp1 + key;
                    }

                    p2 = dp2 + key;

                    // Console.WriteLine("p dispatch : " + p);
                    Facade.EVT.SimpleDispatch(p, value);
                    if (p.Equals(p2) == false)
                    {
                        // Console.WriteLine("p2 dispatch : " + p2);
                        Facade.EVT.SimpleDispatch(p2, value);
                    }
                }
                else
                {

                    object provalue = null;
                    bool isproxy = false;

                    if (isDictionary(type))
                    {
                        provalue = ((IDictionary)to)[key];
                        isproxy = null != provalue;
                    }
                    else
                    {
                        var fi = type.GetField(key);
                        if (null != fi)
                        {
                            provalue = fi.GetValue(to);
                            p = dp1 + key + ".";
                            isproxy = null != provalue && (provalue is ProxyDataBase || isDictionary(provalue.GetType()));
                        }
                    }

                    p2 = dp2 + key + ".";
                    if (isproxy)
                    {
                        DispatchCopyEdit((ASObject)value, provalue, p, p2);
                    }
                }
            }
        }
    }
}